//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2005 Siemens AG
//
// Version: 1.1.2.5
// Date:    11. Jan. 2006
//////////////////////////////////////////////////////////////////////////////

#include "precomp.h"

//////////////////////////////////////////////////////////////////////////////
#ifdef SAP_SUPPORTED
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// RIL IOCTL - Interface
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
HRESULT RILDrv_EnterSAPMode(DWORD dwParam, const LPRILENTERSAPMODE lpEnterSapMode) {
CRilInstanceHandle* pHandle = ExtractHandle(dwParam);

  if (!pHandle) {
    return E_HANDLE;
  }
  if (!lpEnterSapMode) {
    return E_POINTER;
  }
  if (lpEnterSapMode->dwDataMode == RIL_SAPDATAMODE_XSAP) {
    return E_INVALIDARG;
  }
  DWORD dwBeaconPeriod = 0;
  if (lpEnterSapMode->bAutoReconnect) {
    // Read the beacon period from the registry
    if (!GetRegistryDWORD(HKEY_LOCAL_MACHINE, g_tszRegKeyRIL, SAPBeaconRegKey, &dwBeaconPeriod)) {
      dwBeaconPeriod = 6;
    }
  }
  if (!pHandle->GetDevice()->GetRsa()->Enter(pHandle, (lpEnterSapMode->dwSAPMode == RIL_SAPMODE_SERVER), dwBeaconPeriod)) {
    return E_FAIL;
  }
  return S_OK;
}

//////////////////////////////////////////////////////////////////////////////
HRESULT RILDrv_ExitSAPMode(DWORD dwParam, BOOL bForce) {
CRilInstanceHandle* pHandle = ExtractHandle(dwParam);

  if (!pHandle) {
    return E_HANDLE;
  }
  if (!pHandle->GetDevice()->GetRsa()->Exit(pHandle, bForce)) {
    return E_FAIL;
  }
  return S_OK;
}

//////////////////////////////////////////////////////////////////////////////
//
// CRsaHandling - Implementation
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// Public interface
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// Constructor
//
//////////////////////////////////////////////////////////////////////////////
CRsaHandling::CRsaHandling() {
  InitializeCriticalSection(&m_csLock);
  m_hExitEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
  m_pRilDevice = 0;
  ResetStates();
}

//////////////////////////////////////////////////////////////////////////////
//
// Destructor
//
//////////////////////////////////////////////////////////////////////////////
CRsaHandling::~CRsaHandling() {
  Deinit();
  DeleteCriticalSection(&m_csLock);
}

//////////////////////////////////////////////////////////////////////////////
//
// Initialize the Remote SIM Access handling.
//
// Params:
//  pRilHandle: Pointer to the connected CRilHandle instance which is needed
//              internally.
//
// Return:
//  TRUE or FALSE depending on the success of the operation.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CRsaHandling::Init(CRilHandle* pRilDevice) {
  SYNCBLOCK(m_csLock);
  m_pRilDevice = pRilDevice;
  return m_pRilDevice ? TRUE : FALSE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Stop the Remote SIM Access handling.
//
// Return:
//  TRUE or FALSE depending on the success of the operation
//
//////////////////////////////////////////////////////////////////////////////
BOOL CRsaHandling::Deinit(void) {
  SYNCBLOCK(m_csLock);
  m_pRilDevice = 0;
  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Reset all internal states.
//
//////////////////////////////////////////////////////////////////////////////
void CRsaHandling::ResetStates(void) {
  SYNCBLOCK(m_csLock);
  m_dwSapRole = SAP_ROLE_NONE;
  m_dwLastSapRole = SAP_ROLE_NONE;
  SetEvent(m_hExitEvent);
}

//////////////////////////////////////////////////////////////////////////////
//
// Enter the Remote SIM Access mode.
//
// Params:
//  fServer       : Flag indicating if RSA shall be entered in server mode.
//                  If FALSE is passed RSA is entered in client mode.
//  dwBeaconPeriod: Time in seconds to use for automatic reconnection
//                  attempts.
//  pHandle       : Pointer to CRilInstanceHandle to be used for the
//                  operation.
//
// Return:
//  TRUE or FALSE depending on the success of the operation.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CRsaHandling::Enter(CRilInstanceHandle* pInstance, BOOL fServer, DWORD dwBeaconPeriod) {
char  szCmd[MAX_ATCMD_LEN];

  // Entering RSA mode is only allowed if the RSA port has already been opened
  if (!g_ComManager.IsRsaPortUnlocked()) {
    RILRetailTrace((TEXT("RilDrv: Entering SAP mode not possible because virtual RSA port hasn't been opened\r\n")));
    return FALSE;
  }

  SYNCBLOCK(m_csLock);

  if (m_dwSapRole != SAP_ROLE_NONE) {
    RILRetailTrace((TEXT("RilDrv: SAP is already %s %s mode\r\n"), (m_dwLastSapRole == SAP_ROLE_NONE) ? TEXT("waiting for") : TEXT("in"), (m_dwSapRole == SAP_ROLE_SERVER) ? TEXT("server") : TEXT("client")));
    return FALSE;
  }

  m_dwLastSapRole = m_dwSapRole;
  m_dwSapRole = fServer ? SAP_ROLE_SERVER : SAP_ROLE_CLIENT;
  sprintf(szCmd, "AT^SRSA=%d,%d,%d,1,%d\r", m_dwSapRole, m_dwSapRole, g_ComManager.GetRsaMuxChn(), dwBeaconPeriod);

  // Queue the command
  if (pInstance) {
    HRESULT  hr;
    if (!QueueCmd(SAP_CMD_PORT, pInstance, szCmd, CMDOPT_NONE, APIID_SAP, ParseResult, NULL, hr, 0, this)) {
      return FALSE;
    }
  } else {
    if (!QueueInternalCmd(SAP_CMD_PORT, m_pRilDevice, szCmd, CMDOPT_NONE, APIID_SAP, 0, 0, 0, ParseResult, 0, this)) {
      return FALSE;
    }
  }

  ResetEvent(m_hExitEvent);

  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Exit the Remote SIM Access mode.
//
// Params:
//  fForce : Flag indicating if RSA closing shall be forced ignoring any
//           possible error.
//  pHandle: Pointer to CRilInstanceHandle to be used for the operation.
//
// Return:
//  TRUE or FALSE depending on the success of the operation.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CRsaHandling::Exit(CRilInstanceHandle* pInstance, BOOL fForce) {
char  szCmd[MAX_ATCMD_LEN];

  // Lock the virtual RSA port if exit is forced
  if (fForce) {
    g_ComManager.LockRsaPort(FALSE, FALSE);
  }

  SYNCBLOCK(m_csLock);

  if ((m_dwSapRole == SAP_ROLE_NONE) && (m_dwLastSapRole == m_dwSapRole)) {
    RILRetailTrace((TEXT("RilDrv: SAP is already in idle mode\r\n")));
    return FALSE;
  }

  if (!fForce && (m_dwSapRole == SAP_ROLE_NONE)) {
    RILRetailTrace((TEXT("RilDrv: SAP is already waiting for idle mode\r\n")));
    return FALSE;
  }

  if (m_dwSapRole != SAP_ROLE_NONE) {
    m_dwLastSapRole = m_dwSapRole;
    m_dwSapRole = SAP_ROLE_NONE;
  }

  sprintf(szCmd, "AT^SRSA=%d,0,,,,%d\r", m_dwLastSapRole, (fForce ? 1 : 0));

  // Queue the command
  if (pInstance) {
    HRESULT  hr;
    if (!QueueCmd(SAP_CMD_PORT, pInstance, szCmd, CMDOPT_NONE, APIID_SAP, ParseResult, NULL, hr, 0, this)) {
      return FALSE;
    }
  } else {
    if (!QueueInternalCmd(SAP_CMD_PORT, m_pRilDevice, szCmd, CMDOPT_NONE, APIID_SAP, 0, 0, 0, ParseResult, 0, this)) {
      return FALSE;
    }
  }

  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Wait for the RSA mode to be left.
//
// Params:
//  dwTimeout: Timeout in millsecs.
//
// Return:
//  TRUE or FALSE depending on the success of the operation.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CRsaHandling::WaitForExit(DWORD dwTimeout) {
  return (WaitForSingleObject(m_hExitEvent, dwTimeout) == WAIT_OBJECT_0);
}

//////////////////////////////////////////////////////////////////////////////
//
// Parsing function called to parse a RSA notification.
//
// Params:
//  rszPointer: Pointer to the received SAT notification.
//
// Return:
//  TRUE or FALSE depending on the success of the operation.
//
//////////////////////////////////////////////////////////////////////////////
BOOL CRsaHandling::ParseNotification(LPCSTR& rszPointer, COM_PORT_TYPE PortType) {
LPCSTR  szDummy;
UINT    nId, nRole, nState;
BOOL    fChanged;

  if (!ParseUInt(rszPointer, FALSE, nId, rszPointer)    ||
      !ParseRspParamSeparator(rszPointer, rszPointer)   ||
      !ParseUInt(rszPointer, FALSE, nRole, rszPointer)  ||
      !ParseRspParamSeparator(rszPointer, rszPointer)   ||
      !ParseUInt(rszPointer, FALSE, nState, rszPointer)) {
    return FALSE;
  }

  // Look for the "<postfix>" (don't increase string pointer, postfix is scanned again in global response handler)
  if (!ParseRspPostfix(rszPointer, szDummy)) {
    return FALSE;
  }

  // Ignore any notifications on other ports than our command port
  if (PortType != SAP_CMD_PORT) {
    return TRUE;
  }

  SYNCBLOCK(m_csLock);

  fChanged = FALSE;
  if (m_dwLastSapRole != m_dwSapRole) {
    // We're waiting for a change -> check if it occurred
    if (m_dwSapRole != SAP_ROLE_NONE) {
      // We're waiting for a connect
      if (nState) {
        // Yes, here it is
        RILRetailTrace((TEXT("RilDrv: Entered SAP %s mode\r\n"), (m_dwSapRole == SAP_ROLE_SERVER) ? TEXT("server") : TEXT("client")));
        fChanged = TRUE;
      }
    } else {
      // We're waiting for a disconnect
      if (!nState) {
        // Yes, here it is
        RILRetailTrace((TEXT("RilDrv: Left SAP %s mode\r\n"), (m_dwSapRole == SAP_ROLE_SERVER) ? TEXT("server") : TEXT("client")));
        SetEvent(m_hExitEvent);
        g_ComManager.LockRsaPort(FALSE, TRUE);
        fChanged = TRUE;
      }
    }
  } else {
    // We're waiting not for a change -> check if anything unexpected happened
    if (m_dwSapRole != SAP_ROLE_NONE) {
      // We're connected
      if (!nState) {
        // We somehow lost the connection
        RILRetailTrace((TEXT("RilDrv: Unsolicited left SAP %s mode\r\n"), (m_dwSapRole == SAP_ROLE_SERVER) ? TEXT("server") : TEXT("client")));
        SetEvent(m_hExitEvent);
        g_ComManager.LockRsaPort(FALSE, TRUE);
        fChanged = TRUE;
      }
    } else {
      // We're disconnected
      if (nState) {
        // We mysteriously got connected, that should never happen
        RILRetailTrace((TEXT("RilDrv: Unsolicited entered SAP %s mode\r\n"), (m_dwSapRole == SAP_ROLE_SERVER) ? TEXT("server") : TEXT("client")));
        fChanged = TRUE;
      }
    }
  }

  if (fChanged) {
    m_dwLastSapRole = m_dwLastSapRole;
    if (m_pRilDevice) {
      m_pRilDevice->BroadcastDWORDBlobNotification(RIL_NOTIFY_DEVSPECIFIC_SAP, nState ? SAP_CONNECTED : SAP_DISCONNECTED);
    }
  }

  return TRUE;
}

//////////////////////////////////////////////////////////////////////////////
//
// Private interface
//
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
//
// Parsing function for command results.
//
// Return:
//  S_OK on successs, E_FAIL otherwise.
//
//////////////////////////////////////////////////////////////////////////////
HRESULT CRsaHandling::ParseResult(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam) {
CRsaHandling* pInstance = (CRsaHandling*)pParam;
UINT         nResult;

  pBlob = 0;
  cbBlob = 0;

  if (!pInstance) {
    return E_FAIL;
  }

  if (!ParseRspPrefix(szRsp, szRsp)                   ||
      !MatchStringBeginning(szRsp, "^SRSA: ", szRsp)  ||
      !ParseUInt(szRsp, FALSE, nResult, szRsp)        ||
      !ParseRspPostfix(szRsp, szRsp)) {
    return E_FAIL;
  }

  SYNCBLOCK(pInstance->m_csLock);

  if (nResult) {
    RILRetailTrace((TEXT("RilDrv: Failed to %s SAP %s mode\r\n"), (pInstance->m_dwSapRole == SAP_ROLE_NONE) ? TEXT("leave") : TEXT("enter"), (pInstance->m_dwSapRole == SAP_ROLE_SERVER) ? TEXT("server") : TEXT("client")));
    pInstance->m_dwSapRole = pInstance->m_dwLastSapRole;
    return E_FAIL;
  }

  RILRetailTrace((TEXT("RilDrv: %s SAP %s mode successfully initiated\r\n"), (pInstance->m_dwSapRole == SAP_ROLE_NONE) ? TEXT("Leaving") : TEXT("Entering"), (pInstance->m_dwSapRole == SAP_ROLE_SERVER) ? TEXT("server") : TEXT("client")));

  return S_OK;
}

//////////////////////////////////////////////////////////////////////////////
#endif // SAP_SUPPORTED
//////////////////////////////////////////////////////////////////////////////

